﻿using System;
using System.Collections.ObjectModel;
using System.Linq.Expressions;
using RatingAuthor;

namespace ConditionTreeTestStand
{
  public abstract class Column
  {
    public static readonly ReadOnlyCollection<Column> BookColumns =
      new ReadOnlyCollection<Column>(new[]
        {
          BookColumn("Титул", b => b.Title),
          BookColumn("Атвор", b => b.Author.Name),
          BookColumn("Коммент.", b => b.CommentsCount),
          BookColumn("Оценок", b => b.MarksCount),
          BookColumn("Размер", b => b.Size),
          BookColumn("Жанр", b => b.Ganres),
          BookColumn("Оценка", b => b.Mark),
        });

    public abstract string Name { get;}
    public abstract object GetValue(object arg);
    public abstract OperandType Type { get;}
    public abstract Expression Expression { get; }

    #region ' Implementation '

    public override string ToString()
    {
      return Name;
    }
    private static Column BookColumn<TResult>(string name, Expression<Func<Book, TResult>> selector)
    {
      return new TypedColumn<Book, TResult>(name, selector);
    }
    private class TypedColumn<TArg, TResult> : Column
    {
      private readonly Func<TArg, TResult> _selector;
      private readonly string _name;
      private readonly OperandType _type;
      private readonly Expression _expression;

      internal TypedColumn(string name, Expression<Func<TArg, TResult>> selector)
      {
        _expression = selector.Body;
        
        if (typeof(TResult) == typeof(GenresSet))
          _expression = Expression.PropertyOrField(_expression, "_data");

        _selector = selector.Compile();
        _name = name;
        _type = typeof(TResult).ToOperandType();
      }

      public override string Name
      {
        get { return _name; }
      }
      public override object GetValue(object arg)
      {
        return _selector((TArg) arg);
      }
      public override OperandType Type
      {
        get { return _type; }
      }
      public override Expression Expression
      {
        get { return _expression; }
      }
    }

    #endregion
  }
}